package com.tencent.sr.iris.activity.common.util;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.tencent.sr.iris.activity.common.config.QuFaConfig;
import com.tencent.sr.iris.activity.common.enums.IrisErrorCode;
import com.tencent.sr.rmall.common.exception.retail.TRetailBizException;
import com.tencent.sr.rmall.common.exception.retail.TRetailRpcResultException;
import com.tencent.sr.rmall.common.response.PaginationResponse;
import com.tencent.sr.rmall.springbootstarter.utils.JsonUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.springframework.http.HttpMethod;

import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.*;

import static java.nio.charset.StandardCharsets.UTF_8;

/**
 * 区发接口请求工具
 *
 * @Author: guoqingpeng
 * @Date: 2022/5/4 2:36 下午
 */
@Slf4j
public class QuFaStaticHttpUtil {

    private static final String MAC_NAME = "HmacSHA1";
    private static final String ENCODING = "UTF-8";


    public static <T> T postReslutObject(QuFaConfig config, String path, Object body, Class<T> tClass) {
        JSONObject jsonRes = postCommon(config, path, body);
        JSONObject jsonData = jsonRes.getJSONObject("data");
        return JSON.parseObject(jsonData.toJSONString(), tClass);
    }

    public static <T> List<T> postReslutList(QuFaConfig config, String path, Object body, Class<T> tClass) {
        JSONObject jsonRes = postCommon(config, path, body);
        JSONArray jsonData = jsonRes.getJSONArray("data");
        return JSON.parseArray(jsonData.toJSONString(), tClass);
    }

    public static <T> PaginationResponse<T> postPageList(QuFaConfig config, String path, Object body, Class<T> tClass) {
        // 参考接口文档：https://iwiki.woa.com/pages/viewpage.action?pageId=1728747029
        JSONObject jsonRes = postCommon(config, path, body);
        JSONArray jsonData = jsonRes.getJSONObject("data").getJSONArray("data");
        List<T> dataList = JSON.parseArray(jsonData.toJSONString(), tClass);
        PaginationResponse<T> pageRes = new PaginationResponse<>();
        pageRes.setDataList(dataList);
        pageRes.setPageNum(jsonRes.getJSONObject("data").getInteger("current_page"));
        pageRes.setPageSize(jsonRes.getJSONObject("data").getInteger("per_page"));
        pageRes.setTotalCount(jsonRes.getJSONObject("data").getInteger("total"));
        return pageRes;
    }

    public static <T> PaginationResponse<T> newPostPageList(QuFaConfig config, String path, Object body,
            Class<T> tClass) {
        // 参考接口文档：https://iwiki.woa.com/pages/viewpage.action?pageId=1728747029
        JSONObject jsonRes = postCommon(config, path, body);
        JSONArray jsonData = jsonRes.getJSONObject("data").getJSONArray("list");
        List<T> dataList = JSON.parseArray(jsonData.toJSONString(), tClass);
        PaginationResponse<T> pageRes = new PaginationResponse<>();
        pageRes.setDataList(dataList);
        pageRes.setPageNum(jsonRes.getJSONObject("data").getInteger("page"));
        pageRes.setPageSize(jsonRes.getJSONObject("data").getInteger("pageSize"));
        pageRes.setTotalCount(jsonRes.getJSONObject("data").getInteger("total"));
        return pageRes;
    }

    public static <T> PaginationResponse<T> newPageList(QuFaConfig config, String path, Object body, Class<T> tClass) {
        // 参考接口文档：https://iwiki.woa.com/pages/viewpage.action?pageId=1728747029
        JSONObject jsonRes = postCommon(config, path, body);
        JSONArray jsonData = jsonRes.getJSONArray("data");
        List<T> dataList = JSON.parseArray(jsonData.toJSONString(), tClass);
        PaginationResponse<T> pageRes = new PaginationResponse<>();
        pageRes.setDataList(dataList);
        pageRes.setPageNum(0);
        pageRes.setPageSize(0);
        pageRes.setTotalCount(0);
        return pageRes;
    }

    private static JSONObject postCommon(QuFaConfig config, String path, Object body) {
        try {
            String reqBody = JsonUtils.toJsonString(body);
            log.info("QuFaHttpUtil post url:{},body:{}", config.getHost() + path, reqBody);
            String contentType = "application/json";
            String xDate = getGMTTime();
            String contentMD5 = base64Encode(getMD5(reqBody).getBytes());

            URL parsedUrl = new URL(config.getHost() + path);
            String pathAndParams = parsedUrl.getPath();
            if (parsedUrl.getQuery() != null) {
                pathAndParams = pathAndParams + "?" + sortQueryParams(parsedUrl.getQuery());
            }

            String stringToSign = String.format("x-date: %s\n%s\n%s\n%s\n%s\n%s",
                    xDate, HttpMethod.POST.name(), contentType, contentType, contentMD5, pathAndParams);
            byte[] hmacStr = HmacSHA1Encrypt(stringToSign, config.getAppSecret());
            String signature = base64Encode(hmacStr);
            String authHeader = String.format("hmac id=\"%s\", algorithm=\"hmac-sha1\", "
                    + "headers=\"x-date\", signature=\"%s\"", config.getAppKey(), signature);

            HttpPost httpPost = new HttpPost(config.getHost() + path);
            httpPost.setHeader("Accept", contentType);
            httpPost.setHeader("Host", config.getHost().replace("https://", ""));
            httpPost.setHeader("x-date", xDate);
            httpPost.setHeader("Content-Type", contentType);

            httpPost.setHeader("Content-MD5", contentMD5);
            httpPost.setHeader("Authorization", authHeader);
            StringEntity stringEntity = new StringEntity(reqBody, ENCODING);
            httpPost.setEntity(stringEntity);
            CloseableHttpClient httpClient = HttpClients.createDefault();
            CloseableHttpResponse response = httpClient.execute(httpPost);
            HttpEntity responseEntity = response.getEntity();
            if (responseEntity != null) {
                String result = EntityUtils.toString(responseEntity, UTF_8);
                if (result == null) {
                    throw new TRetailBizException(IrisErrorCode.QUERY_QUFA_FAIL);
                }
                JSONObject jsonRes = JsonUtils.parse(result, JSONObject.class);
                if (jsonRes == null) {
                    throw new TRetailBizException(IrisErrorCode.QUERY_QUFA_FAIL);
                }
                Integer code = jsonRes.getInteger("code");
                if (code == null) {
                    log.error("QuFaHttpUtil post code null error,path:{},req:{},result:{}", path, reqBody, result);
                    throw new TRetailBizException(IrisErrorCode.QUERY_QUFA_FAIL, jsonRes.getString("message"));
                } else if (code != 200) {
                    log.error("QuFaHttpUtil post code not 200 error,path:{},req:{},result:{}", path, reqBody, result);
                    throw new TRetailBizException(IrisErrorCode.QUERY_QUFA_FAIL, jsonRes.getString("msg"));
                }
                log.info("QuFaHttpUtil post success,path:{},req:{},result:{}", path, reqBody, result);
                return jsonRes;
            }
            throw new TRetailBizException(IrisErrorCode.QUERY_QUFA_FAIL);
        } catch (Exception e) {
            if (e instanceof TRetailBizException) {
                TRetailBizException exp = (TRetailBizException) e;
                throw new TRetailBizException(exp.getErrorCode(), exp.getMessage());
            }
            log.error("QuFaHttpUtil post exception, path:{},req:{}", path, JsonUtils.toJsonString(body), e);
            throw new TRetailRpcResultException(IrisErrorCode.QUERY_QUFA_FAIL);
        }
    }

    private static String getGMTTime() {
        Calendar cd = Calendar.getInstance();
        SimpleDateFormat sdf = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss 'GMT'", Locale.US);
        sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
        String gmtTime = sdf.format(cd.getTime());
        return gmtTime;
    }

    private static String sortQueryParams(String queryParam) {
        // parameters should be in alphabetical order
        if (queryParam == null || queryParam == "") {
            return "";
        }

        String[] queryParams = queryParam.split("&");
        Map<String, String> queryPairs = new TreeMap<>();
        for (String query : queryParams) {
            String[] kv = query.split("=");
            queryPairs.put(kv[0], kv[1]);
        }

        StringBuilder sortedParamsBuilder = new StringBuilder();
        Iterator iter = queryPairs.entrySet().iterator();
        while (iter.hasNext()) {
            Map.Entry entry = (Map.Entry) iter.next();
            sortedParamsBuilder.append(entry.getKey());
            sortedParamsBuilder.append("=");
            sortedParamsBuilder.append(entry.getValue());
            sortedParamsBuilder.append("&");
        }
        String sortedParams = sortedParamsBuilder.toString();
        sortedParams = sortedParams.substring(0, sortedParams.length() - 1);

        return sortedParams;
    }

    private static byte[] HmacSHA1Encrypt(String encryptText, String encryptKey) throws Exception {
        byte[] data = encryptKey.getBytes(ENCODING);
        SecretKey secretKey = new SecretKeySpec(data, MAC_NAME);
        Mac mac = Mac.getInstance(MAC_NAME);
        mac.init(secretKey);

        byte[] text = encryptText.getBytes(ENCODING);
        return mac.doFinal(text);
    }

    private static String base64Encode(byte[] key) {
        final Base64.Encoder encoder = Base64.getEncoder();
        return encoder.encodeToString(key);
    }

    private static String getMD5(String str) {
        String md5Hex = DigestUtils.md5Hex(str);
        return md5Hex;
    }
}
